/**
 * SNApp is magical!
 */

// base level package
var snapp = {
	// track urls
	actionURLs: {},
	//set default action URL
	setURL: function(e1,e2) {
		if (!e2) {
			this.actionURLs["DEFAULT"] = e1;
		} else {
			this.actionURLs[e1] = e2;
		}
	},
	//get default action URL
	getURL: function(e1) {
		if (!e1) {
			return this.actionURLs["DEFAULT"];
		} else {
			return this.actionURLs[e1];
		}
	},
	
	// "Child Nodes By Name" - recursively
	cnbn: function(node, childNodeName,chnodearr) {
	    
	    if (!chnodearr) chnodearr = new Array();
	    
	    for (node.itr = 0; node.itr < node.childNodes.length; node.itr++) {
	        if (node.childNodes[node.itr].nodeType == 1) {
		        if (node.childNodes[node.itr].attributes['name']) { 
			        if (node.childNodes[node.itr].attributes['name'].value == childNodeName)
			            chnodearr.push(node.childNodes[node.itr]);
	        	}
	        }
	        snapp.cnbn(node.childNodes[node.itr],childNodeName,chnodearr);
	    }
	    return chnodearr;
	}
}

/**
 * 
 *  SNApp catalog package.
 * 
 * 	Static methods for SNApp catalog access.
 * 
 */ 
snapp.catalog = {
	list_cache: new Object(),
	bean_cache: new Object(),
	// Retrieve an item from the catalog.
	get: function(mnemonic, onLoad) {
		if (!this.bean_cache[mnemonic]) {
			snob = this.getSnob();
			// set return function
			snob.cat_onLoad = onLoad;
			// load the object for the given mnemonic
			snob.load( {
				'mnemonic': mnemonic
			} );
			
		}
		else {
			if (typeof onLoad == 'string') {
				eval_str = onLoad+'(this.bean_cache[mnemonic])';
				eval(eval_str);
			} else {
				onLoad(this.bean_cache[mnemonic]);
			}
		}
	},
	
	list: function(mnemonic, onLoad) {
		if (!this.list_cache[mnemonic]) {
			snob = this.getSnob();
			// set mnemonic to snob list 
			snob.setProperty("mnemonic",mnemonic);
			
			// set return function
			snob.cat_onLoad = onLoad;
			
			// load the object for the given mnemonic
			snob.list( {
				'mnemonic': mnemonic
			} );
			
		}
		else {
			if (typeof onLoad == 'string') {
				eval_str = onLoad+'(this.list_cache[mnemonic])';
				eval(eval_str);
			} else {
				onLoad(this.list_cache[mnemonic]);
			}
		}
	},
	
	evaluateResponse: function(curr_snob) {
		if (curr_snob.isArray) {
			if (!this.list_cache[curr_snob.getProperty('mnemonic')])
				this.list_cache[curr_snob.getProperty('mnemonic')] = curr_snob;
				
			for (itr=0;itr<curr_snob.getArray().length;itr++) {
				nxt = curr_snob.getArray()[itr];
				mnemonic = nxt.getProperty('mnemonic');
				if (!this.bean_cache[nxt.getProperty('mnemonic')])
					this.bean_cache[mnemonic]=nxt;
			}
		} else {
			mnemonic = curr_snob.getProperty('mnemonic');
			if (!this.bean_cache[curr_snob.getProperty('mnemonic')])
				this.bean_cache[mnemonic]=curr_snob;
		}
		
		if (typeof curr_snob.cat_onLoad == 'string') {
			eval_str = curr_snob.cat_onLoad+'(curr_snob)';
			eval(eval_str);
		} else {
			curr_snob.cat_onLoad(curr_snob);
		}
	},
	
	getSnob: function() {
		// response function
		rf = catalog.evaluateResponse.bind(this);
		
		// get a new snobbject
		snob = new snapp.Object({
			url: snapp.getURL("snapp_util"),
			
			loadAction: 'catalog.Load',
			listAction: 'catalog.List',
			
			'onLoad': rf,
			'onList': rf
		});
		
		snob.setProperty("SNAppGlobalId",'SNAPP_CATALOG');
		
		return snob;
	}	
}

/**
 * SNApp objects package...
 * 
 * This package contains all of the classes/procedures
 * that represent connected db objects.
 */
snapp.objects = {
	// connected object sequence (for identification)
	SEQUENCE: 0

};
	/*
	 * Base Connectivity class
	 * 	- Represents a snapp connection
	 */
snapp.Object = new Class({
	Implements: Options,
	
	/*
	 * Customizable options
	 */
	options: {
		// response format
		responseFormat: 'xml',
		
		// actions to call for each operation
		actions: {
			load: 'Load',
			list: 'List',
			count: 'Count',
			save: 'Save',
			saveList: 'SaveList'
		},
		
		// Events for call completion
		// Signature:  callBack(this object, responseXML, responseText)
		events: {
			load: null,
			list: null,
			count: null,
			save: null,
			saveList: null,
				
		},
		
		// Request events.  See Mootools "Request" object events.
		onRequest: null,
		onLoadStart: null,
		onProgress: null,
		onComplete: null,
		onCancel: null,
		//onSuccess is overwritten by snapp DA
		onFailure: null,
		onException: null
	},
	
	/*
	 * "Data Transfer Object" 
	 * 
	 * - This object will contain all properties associated
	 *	with storage and retrieval; no more, no less.  
	 *	Any attributes that need to be parameterized will
	 *	exist within this object.  
	 *
	 *	The "setProperty" and "getProperty" methods refer to 
	 *	the DTO properties.
	 *
	 */ 
	DTO: new Object(),
	getDTO: function() {return this.DTO;},
	
	/*
	 * Contains the property keys - Necessary for iteration!
	 */
	keys: new Array(),
	
	/*
	 * Segregated list of child objects
	 */
	objectList: new Array(),
	
	/*
	 * Mootools Request object.  This will remain stateful within the snapp.Object.
	 */
	request: null,
	
	/*
	 * Current event name.  
	 */
	eventName: null,
	
	/**
	 * @param Object to parse
	 * @param Options object
	 */
	initialize: function() {
		if (arguments.length > 1) {
			obj2parse = arguments[0];
			options = arguments[1];
		} else {
			options = arguments[0];
		}
		this.setOptions(options);
		if (!this.options.url) this.options.url = snapp.getURL();
		
		// unique sequence id
		snapp.objects.SEQUENCE++;
		this.SNAppID = snapp.objects.SEQUENCE;
		
		// create actions..
		this.createActions();
		
	},

	/*
	 * Create an action for each listed in options
	 */
	createActions: function() {
		// for each action listed
		for (oa in this.options.actions) {
			act = this.options.actions[oa];
			if (act) {
				// set caller
				this[act] = function(params) {
					if (typeof(params) == 'element') {
						if (params.nodeName.toUpperCase() == 'FORM') {
							this.fromForm(params);
							params = this.DTO;
						} else {
							alert('Not a form!');
						}
					}
					// check for id property for save
					if (params) {
						// if identifier is not present, set it.
						if (!params[this.getProperty('SNAppIdName')]
							&& this.getProperty(this.getProperty('SNAppIdName')))
							params[this.getProperty('SNAppIdName')] = this.getProperty(this.getProperty('SNAppIdName')); 
					}

					this.sendRequest(act, params);
				}
			}			
		}
	},
	
	//set attribute
	setProperty: function(snattr,value) {
		this.DTO[snattr] = value;	
		if (!this.keys.contains(snattr)) this.keys.push(snattr);
	},
	
	//Returns the attribute (may be any acceptable js object)
	getProperty: function(snattr) {
		return this.DTO[snattr];	
	},
	
	
	/********************************************************************************
	 *  Object Utilities
	 */
	 
	/*
	 * Since all snapp.objects.Objects are objects, and act as objects,
	 * arrayed snapp.objects.Objects must be called using this method...
	 * A simple array of snapp.objects.Objects is returned
	 */
	getArray: function() {
		return this.SNArray;	
	},
	
	containsKey: function(elnm) {
		return this.keys.indexOf(elnm) > -1;
	},
	
	containsValue: function(obj){
        for(var i=0; i<this.keys.length; i++){
            if(this.keys[i]==obj){
                return true;
            }
        }
        return false;
    },
    	
	// recursively gets an array of ALL children disregarding the layer
	getAllChildren: function() {
		this.child_array = null;
		arr = new Array();	
		this._getNextTier(arr);
		return arr;	
	},

    // Recursively search each tier for an object with the specified property and value
    findObject: function(elnm,elval) {
		return this._searchNextTier(elnm,elval);	
	},
	
	containsObject: function(elnm,elval) {
		return this.getProperty(elnm) == elval || 
			(this.findObject(elnm,elval) != null && typeof this.findObject(elnm,elval) != 'undefined'); 	
	},
	
	findArray: function(elnm,elval) {
		return this.findObject(elnm,elval).SNArray;
	},

	/********************************************************************************
	 * UI Utilities
	 */
	
	// Asynchronously save a form and populate the object based on the response
	saveForm: function(frm) {
		_frame = snapp.form.aSyncSubmit(frm);
		_frame.snob = this;
		_frame.onload = function() {
			
			//alert('ASyncSubmit content: '+this.contentDocument.innerHTML);
			this.snob.DA.processResults(this.contentDocument.innerHTML,this.contentDocument.childNodes[0]);
			
		}	
	},
	 
	// Populate this object's properties based on form values
	fromForm: function(frm) {
		// set properties of object based on form values
		this.els = frm.elements;
		for (this.itr=0;this.itr<this.els.length;this.itr++) {
			this.fel = this.els[this.itr];
			//logger.info('Form element Type: '+this.fel.type);
			switch(this.fel.type) {
				case "text":
				case "textarea":
				case "hidden":
				case "password":
					logger.info('setting property "'+this.fel.name+'" to "'+ this.fel.value + '"',snapp.objects.Object.fromForm);
					this.setProperty(this.fel.name, this.fel.value);
					break;
				case "select-one":
					this.opts = this.fel.options;
					this.selOpt = this.opts[this.opts.selectedIndex];
					this.setProperty(this.fel.name,this.selOpt.value);
					logger.info('found selector: '+ this.fel.name + ' = ' + this.selOpt.value);
					break;
				case "select-multiple":
					this.arr = new Array();
					this.opts = this.fel.options;
					for (fe=0;fe<this.opts.length;fe++) {
						this.opt = this.opts[fe];
						if (this.opt.selected) this.arr.push(this.opt.value);
					}
					this.setProperty(this.fel.name+'_values',this.arr);
					break;
				case "checkbox":
					this._tstval = frm.elements[this.fel.name][0] ? frm.elements[this.fel.name][0].value : frm.elements[this.fel.name].value;
					if (this._tstval == this.fel.value) {
						this.boxArr = new Array();
						this.boxes = frm.elements[this.fel.name];
						if (this.boxes.length) {
							for (fe=0;fe<this.boxes.length;fe++) {
								this.box = this.boxes[fe];
								if (this.box.checked) this.boxArr.push(this.box.value);
							}
						} else {
							if (this.boxes.checked) this.boxArr.push(this.boxes.value);
						}
						this.setProperty(this.fel.name+'_values',this.boxArr);							

					}
					break;
				case "radio":
					if (this.fel.checked) {
						this.setProperty(this.fel.name,this.fel.value);
					}
					break;
				default:
					break;
			}
		}
		//logger.openLog();
		frm.snob = this;
	},
	
	// Populate form values based on this object's properties
	toForm: function(frm) {
		// save reference to this dbo
		frm.snob = this;
		// set form values based on this object's properties
		this.els = frm.elements;
		for (this.itr=0;this.itr<this.els.length;this.itr++) {
			this.fel = this.els[this.itr];
			//this.doValidation(this.fel);
			switch(this.fel.type) {
				case "text":
				case "textarea":
				case "hidden":
				case "password":
					if (this.getProperty(this.fel.name) != null) {
						this.fel.value = this.getProperty(this.fel.name);
					}
					break;
				case "select-one":
				case "select-multiple":
					try {
						se = this.getProperty(this.fel.name);
						if (se) {
							this.sa = se;
							if(typeof se == 'string') {
								this.sa = se.split(',');
							} else if (se.isArray) {
								this.sa = new Array();
								for(s=0;s<se.getArray().length;s++)
									this.sa.push(se.getArray()[s].getProperty(se.getArray()[s].getProperty('SNAppIdName'))); 	
							}
							logger.info('Found selector value > '+this.sa.toString());
							this.opts = this.fel.options;
							for (this.o=0;this.o<this.opts.length;this.o++) {
								if (this.sa.indexOf(this.opts[this.o].value) > -1)
									this.opts[this.o].selected = true;
							}
						}
					} catch(e) {
						logger.warn("Error building selector "+this.fel.name,'snapp.objects.Object.toForm',e);
					}
					break;
				case "checkbox":
				case "radio":
					try {
						if (!this[this.fel.name+'_selectionarray']) {
							se = this.getProperty(this.fel.name);
							if (se) {
								this[this.fel.name+'_selectionarray'] = se;
								if(typeof se == 'string') {
									this[this.fel.name+'_selectionarray'] = se.split(',');
								} else if (se.isArray) {
									this[this.fel.name+'_selectionarray'] = new Array();
									for(s=0;s<se.getArray().length;s++) {
										this[this.fel.name+'_selectionarray'].push(se.getArray()[s].getProperty(se.getArray()[s].getProperty('SNAppIdName')));
									} 	
								} else {
									this.fel.checked = false;
								}
							} else {
								this.fel.checked = false;
							}
							
						}
						
						if (this[this.fel.name+'_selectionarray'].indexOf(this.fel.value) > -1)
							this.fel.checked = true;
						else
							this.fel.checked = false;
					
					} catch(e) {
						this.fel.checked = false;
						logger.warn("Error building checkbox/radio "+this.fel.name,'snapp.objects.Object.toForm',e);
					}
					break;
				default:
					break;
			}
		
		}
		//logger.openLog();
	},

	/**
	 * Populate a display area by matching property names to element names
	 */
	toDisplay: function(dispNode) {
		// go through properties
		for (p=0;p<this.keys.length;p++) {
			prop = this.keys[p];
			val = this.getProperty(prop);
			
			// get elements with property name
			pels = snapp.cnbn(dispNode,prop);
			for (pel=0;pel<pels.length;pel++) {
				pels[pel].innerHTML = val;
			}
		}
	},
	
	/**
	 * Build a selector from this snapp.objects.Object using the given "select" element and configuration
	 */
	toSelector: function(selOb,options) {
		this.selector = new snapp.form.Selector(this, selOb, options);				
		this.selector.buildOptions(selOb);		
	},
	
	/**
	 * Build a checkbox selection area from this list given the wrapper element and configuration
	 */
	toCheckboxList: function(wrpr,options) {
		this.checkboxlist = new snapp.form.CheckboxList(this, wrpr, options);
		this.checkboxlist.build();
	},



	/**************************************************************************
	 * Send the custom request given action and parameters
	 */
	//send ajax given action & Parameters
	sendRequest: function(action, params) {
		// set the current eventName
		this.eventName = eventName;
		// reset parameters
		this.parameters = '';
		// if an action is provided, use it
		if (this.options.actions[this.eventName]) {
			//set the requested action
			this.parameters = 'action=' + this.options.actions[this.eventName];
		// otherwise, we have no action.  That doesn't work
		} else {
			logger.error('no ' + this.eventName + ' action set for snapp.Object!');
		}
		
		// append the snapp global identifier
		try {
			this.parameters += '&SNAppGlobalId='+this.getProperty('SNAppGlobalId');	
		// we need the snapp global id
		} catch(e) {
			//logger.error('Error making '+eventName+' call for dbo. NO "SNAppGlobalId" property set.',snapp.objects.Connection);
			return;
		}
		// if additional parameters are provided...
		if (params) {
			this.parameters += Object.toQueryString(params);
		} else {
			this.parameters += Object.toQueryString(this.DTO);
		}
		if (!this.request) {
			this.request = new Request({
				method: 'post',
				link: 'cancel', // This will cancel the running request and start the new one
				onSuccess: function(responseText, responseXML) {
					if (this.options.responseFormat == 'xml') {
						// parse XML
						this.XMLP = new XMLParser(responseXML);
						
					} else if (this.options.responseFormat == 'json') {
						// js parser
						this.JSP = new JSParser(eval('"'+responseText+'"'));
					}
					// call completion event
					if (this.options.events[this.eventName]) {
						this.options.events[this.eventName](this, responseXML, responstText);
					}
				}.bind(this)
			});
			// copy all necessary events to Request
			this.request.setOptions(this.options);
		}
		this.request.send(this.parameters);
		//logger.info('Sending request: (url+params) > ' + this.options.url + '?' + this.parameters, snapp.objects.Connection);
	},
		
	/******************************************************************
	 * XML parser class
	 * 
	 * This class is always bound to snapp.Object
	 * and is referenced as this.XMLP.  
	 * This ensures that only one instance of this class can exist
	 * at any given time within the snapp.Object.
	 */
	XMLParser: new Class({
		// parse the results..
		parseResponse: function() {		
			
			resp_node = this.responseXML.firstChild;
			// set result data
			this.resultNode = resp_node.getElementsByTagName("results")[0];
			this.resultCode = parseInt(this.resultNode.attributes.getNamedItem("code").value);
			this.resultMessage = this.resultNode.attributes.getNamedItem("message").value;
			this.responseBody = resp_node.getElementsByTagName("body")[0].childNodes[0];
			
			try {
				// Build the first object
				bob = this.XMLP.buildObject(this.responseBody,this);
				// Kick off recursion
				this.XMLP.recurseXML(bob,this.responseBody);
				
			} catch(e) {
				//no response...  which can be ok.
				logger.warn("Could not parse response.  Result Code = "+bob.resultCode+";  Result Message: "+bob.resultMessage, snapp.objects.Parser, e);
			}
		},

		// build a new object, unless provided
		buildObject: function(nd,snob) {
			// if we are given an object, use it
			if (snob) {
				bob = snob;
			
			// otherwise, test for a global name to instantiate next object
			} else {
				if (nd.attributes.getNamedItem("SNAppGlobalName") != null) {
					try {
						eval('bob = new '+nd.attributes.getNamedItem("SNAppGlobalName").value+'();');
					} catch(e) {
						//logger.warn('Could not find bean > '+nd.attributes.getNamedItem("SNAppGlobalName").value, snapp.objects.Parser);
						bob = new snapp.Object();
					}
				} else {
					bob = new snapp.Object();
				}	
			}
			
			// Test for array, and set accordingly
			if (this.nodeIsArray(nd)) {
				bob.isArray = true;
				bob.SNArray = new Array();
				if (nd.attributes.getNamedItem("array") == null) bob.array = nd.nodeName;	
			}
			
			// Set properties based on attributes
			snatts = nd.attributes;
			for (snatt=0;snatt<snatts.length;snatt++) {
				atrbt = snatts.item(snatt);
				bob.setProperty(atrbt.name.toString(),atrbt.value.toString()); 
		 	}
		 	// set the node name to the object
			bob.NODENAME = nd.nodeName;
			
			return bob;
		},	

		// test to see if the current node is an arrayk
		nodeIsArray: function(nnd) {
			_rv = false;
			// get child nodes
			_chs = nnd.childNodes;
			if (_chs) {
				// gather child nodes into a copy
				_chnodes = new Array();
				for (ch1=0;ch1<_chs.length;ch1++) {
					if (parseInt(_chs.item(ch1).nodeType) == 1) {
						_chnodes.push(_chs.item(ch1));
					}
				}
				// if we have more than one child...
				if (_chnodes.length > 1) {
					// default is to assume they are the same name
					same = true;
					// test to verify all children are named the same
					for(ch2=0;ch2<_chnodes.length;ch2++) {
						nxt = _chnodes[ch2];
						// set the first child name
						if (ch2 == 0) ndnm = nxt.nodeName;
						// if we do not match the name, this is not an array.
						if (ndnm != nxt.nodeName) same = false;
					}
					// if all child nodes are named the same, we have an array
					if (same) {
						_rv = true;
					}
				
				// if we have only one child, default to array
				} else if (_chnodes.length == 1) {
					_rv = true;
				}
			}
			return _rv;
		},

		// Base for recursively building the next layer
		recurseXML: function(ob,node) {
			// gather direct child nodes
			chnodes = this.XMLP.getNodeChildren(node);	
			// build the objects for each direct child node
			chobjs = this.XMLP.buildChildObjects(chnodes);		
			// add the children to the object
			this.XMLP.addNodes(ob,chobjs,chnodes);
		},
		
		// Get the direct child nodes for the given node
		getNodeChildren: function(node,pob) {
			// empty copy
			nl = new Array();		
			// child nodes
			chs = node.childNodes;
			if (chs) {
				for (ch=0;ch<chs.length;ch++) {
					nxt = chs.item(ch);
					// if it is an element, add it to the copy
					if (parseInt(nxt.nodeType) == 1) {
						nl.push(nxt);
					}  
				}
			}
			return nl;
		},
		
		// Build objects for each given node.
		buildChildObjects: function(chlds) {
			// empty container for new disconnected objects
			oa = new Array();		
			for (ch=0;ch<chlds.length;ch++) {
				// build the disconnected object and add it
				ob = this.XMLP.buildObject(chlds[ch]);
				oa.push(ob);
			}		
			return oa;
		},
		
		// Add each node and build the next tier
		addNodes: function(ob,chobjs,chnodes) {	
			for (o=0;o<chnodes.length;o++) {
				this.XMLP.addObject(ob,chobjs[o]);
			}	
			this.XMLP.buildNextTier(chobjs,chnodes);
		},
		
		// Add the individual child object
		addObject: function(ob,newob) {
			if (newob.isArray) {
				ob[newob.array] = newob;
				ob.objectList.push(newob.array);
			} 
			if (ob.isArray) { 
				ob.SNArray.push(newob); 
			} 

			ob[newob.NODENAME] = newob;
			ob.objectList.push(newob.NODENAME.toString());
			
		},

		// Build the next tier of object-node matches
		buildNextTier: function(obs,nds) {
			for (o in nds) {
				nd = nds[o];
				nob = obs[o];	
				// Invoke base recursion 
				this.XMLP.recurseXML(nob,nd);
			}
		}
		
			
	}).bind(this),
		
	/******************************************************************
	 * JS parser class
	 * 
	 * This class is always bound to snapp.Object
	 * and is referenced as this.JSP.  
	 * This ensures that only one instance of this class can exist
	 * at any given time within the snapp.Object.
	 */
	JSParser: new Class({
		// parse the results..
		parseObject: function(obj2parse) {		
			
			if (obj2parse.results) {
				// set result data
				this.resultCode = parseInt(this.results.code);
				this.resultMessage = this.results.message;
				this.responseBody = this.results.body;
			}
			try {
				// Build the first object
				bob = this.buildObject(this.responseBody,this);
				// Kick off recursion
				this.DP.recurseXML(bob,this.responseBody);
				
			} catch(e) {
				//no response...  which can be ok.
				logger.warn("Could not parse response.  Result Code = "+bob.resultCode+";  Result Message: "+bob.resultMessage, snapp.objects.Parser, e);
			}
		},
	}).bind(this),
		
	/*************************************************************************
	 * 
	 * Utility functions
	 */
	_getNextTier: function(arr) {
		if (this.isArray) {
			this._getChildArray(arr);
			
		} else {
			this._getChildObjects(arr);
		}
 		return arr;
	},
	
	_getChildArray: function(arr) {
		for (this.array_itr=0;this.array_itr<this.SNArray.length;this.array_itr++) {
			arr.push(this.SNArray[this.array_itr]);
			this.SNArray[this.array_itr]._getNextTier(arr);	
		}
	},
	
	_getChildObjects: function(arr) {
		for (this.object_itr=0;this.object_itr<this.objectList.length;this.object_itr++) {
			arr.push(this[this.objectList[this.object_itr]]);
			this[this.objectList[this.object_itr]]._getNextTier(arr);	
		}		
	},
	 
	_searchNextTier: function(elnm,elval) {
		this.nexttier_object = null;	
		if (this.isArray) {
			this.nexttier_object = this._searchArray(elnm,elval);
			if (!this.nexttier_object) this.nexttier_object = this._searchArrayTier(elnm,elval);
		} else {
			this.nexttier_object = this._searchObjects(elnm,elval);
			if (!this.nexttier_object && this.objectList.length > 0) this.nexttier_object = this._searchObjectTier(elnm,elval);
		}
 		return this.nexttier_object;	
 	},
	
	_searchArray: function(elnm,elval) {
		
		for (this.array_itr=0;this.array_itr<this.SNArray.length;this.array_itr++) {
			this.activesearch_object = this._searchObject(this.SNArray[this.array_itr],elnm,elval);
			if (this.activesearch_object) return this.activesearch_object;
		}
		return null;
	},
 	
	_searchObjects: function(elnm,elval) {
		for (this.object_itr=0;this.object_itr<this.objectList.length;this.object_itr++) {
			this.activesearch_object = this._searchObject(this[this.objectList[this.object_itr]],elnm,elval);
			if (this.activesearch_object) return this.activesearch_object;
		}
		return null;
	},
	
	_searchObject: function(actpto,elnm,elval) {
		for (e=0;e<actpto.keys.length;e++) {
 			try {
	 			if (actpto.getProperty(elnm) == elval) {
	 				return actpto;
	 			}
 			} catch(e) {}
	 	}
	 	return null;
 	},
	
	_searchArrayTier: function(elnm,elval) {
		for (this.array_itr=0;this.array_itr<this.SNArray.length;this.array_itr++) {
			this.activesearch_object = this.SNArray[this.array_itr].findObject(elnm,elval);
			if (this.activesearch_object) return this.activesearch_object; 
		}
		return null;
	},
	
	_searchObjectTier: function(elnm,elval) {
		for (this.object_itr=0;this.object_itr<this.objectList.length;this.object_itr++) {
			this.activesearch_object = this[this.objectList[this.object_itr]].findObject(elnm,elval);
			if (this.activesearch_object) return this.activesearch_object; 
		}
		return null;
	}

});
	

/*************************************************************************************************************
 * snapp.form package
 *************************************************************************************************************/
snapp.form = {
	//create the illusion of an asynch submission of a form
	aSyncSubmit: function(frm) {
    
	    // create a hidden frame
	    _frame = document.createElement('iframe');
	    _frame.setAttribute('id','hiddenFrame');
	    _frame.setAttribute('name', 'hiddenFrame');
	    _frame.style.display = 'none';
	    frm.parentNode.appendChild(_frame);
	
	    // Damn IE
	    window.frames['hiddenFrame'].name = 'hiddenFrame';
	    // set form target to frame
	    frm.setAttribute('target','hiddenFrame');
	    // submit the form
	    frm.submit();
	
	 	//logger.info('ASyncSubmit Response: '+_frame.innerHTML);
	    return _frame;
	},
	
	selectOption: function(selElement, selVal) {
		this.foundOption = false;
		for (this.o=0;this.o<selElement.options.length;this.o++) {
			if (selElement.options[this.o].value == selVal) {
				selElement.options[this.o].selected = true;
				this.foundOption = true;
			}
		}
		return this.foundOption;
	},
	
	/***********************************************************************
	 * Selector class - build a selector using the given db.Object
	 */
	Selector: new Class({
		Implements: Options,
		
		options: {
			displayProperty: 'name',
			valueProperty: 'id',
			idProperty: 'id',
			altProperty: null,
			selected: null,
			prompt: null,
			all: null,
			trunc: null,
			disabled: false
		},
		
		// constructor
		initialize: function(snob, selOb, options) {
			this.SNObject = snob;
			this.selectElement = $(selOb);
			this.setOptions(options);
		},
		
		/**
		 * Add an option to the select
		 */
		addOption: function (sel,opt){
			
			// format new option...
			o = new Option();
			o.value = opt.getProperty(this.options.valueProperty);
			if (this.options.idProperty)
				o.id = opt.getProperty(this.options.idProperty);
			
			txt = opt.getProperty(this.options.displayProperty);
			
			// check text truncation
			if (this.options.trunc > 0) {
				if (txt.toString().length > parseInt(this.options.trunc)) {
					txt = txt.toString().substring(0,(parseInt(this.options.trunc) - 3)) + "...";
				} else { txt = txt.toString().substring(0,this.options.trunc);}
			}
						
			o.text = txt;
			
			// test for selected value
			o.selected = this.testSelected(opt);
			
			// chek onchange event
			//if (this.OnReselect != null)
				//o.onchange = opt.onchange;
			
			// check for altrnate text
			if (this.options.altProperty) {
				hv = opt.getProperty(this.options.altProperty);
				o.alt = hv;
				o.title = hv;
			}
			
			// push into options array
			sel.options[sel.options.length] = o;
		},
		
		/** test for selected value */
		testSelected: function (opt) {
			// return whether or naot we should be selected
			if (this.selected != null) {
				return opt.getProperty(this.options.valueProperty) == this.selected;
			} else if(this.SNObject.getProperty('selected')) {
				return opt.getProperty(this.options.valueProperty) == this.SNObject.getProperty('selected');
			} 
		},
		
		/** Add prompt */
		addPrompt: function (sel) {
			o = new Option();
			o.value = '-1';
			o.text = this.options.prompt;
			
			// push into options array
			sel.options[sel.options.length] = o;
		},
		
		/** Add all option */
		addAll: function (sel) {
			o = new Option();
			o.value = '0';
			o.text = this.options.all;
			
			// push into options array
			sel.options[sel.options.length] = o;
		},
		
		/** Get selected values */
		getValue: function() {
			selopts = new Array();
			opts = this.selectElement.options;
			for (op=0;op<opts.length;op++) {
				opt = opts[op];
				if (opt.selected) {
					selopts.push(opt.value);
				}
			}
			return selopts.toString();
		},
		
		/**
		 * Build selector options..
		 */				
		buildOptions: function(sel) {
			this.selectElement = sel;
			// set disabled
			sel.disabled = this.options.disabled;
			sel.onmousedown="";
			sel.onmousemove="";
			sel.onmouseup="";
			
			// make sure the options length is reset
			if (!sel.options) sel.options = new Array();
			sel.options.length = 0;
			
			// check for prompt and/or all
			if (this.options.prompt != null) this.addPrompt(sel);
			if (this.options.all != null) this.addAll(sel);	
			
			//alert(this.PTO.ARRAY.length);
			
			if (this.SNObject.getArray() && this.SNObject.getArray().length > 0) {
				// loop and build options...
				for (i=0;i<this.SNObject.getArray().length;i++) {
					this.addOption(sel,this.SNObject.getArray()[i]);
				}
			}
			return sel;
		},
		
		selectOption: function(optVal) {
			for (opt=0;opt<this.selectElement.options.length;opt++) {
				if (this.selectElement.options[opt].value == optVal)
					this.selectElement.options[opt].selected=true;
			}
		}

	}),
	
	/**
	 * Build a checkbox selection area from a db.Object in the given contentElement, using the given config
	 */
	CheckboxList: new Class({
		Implements: Options,
		
		options: {
			
			displayProperty: 'name',
			valueProperty: 'id',
			idProperty: 'id',
			altProperty: null,
			
			displayClass: null,
			boxClass: null,
			cellpadding: null,
			cellspacing: null,
			
			boxname: null,
			onchange: null,
			
			columns: 2
		},
		
		// constructor
		initialize: function(snob, contentElement, options) {
			this.SNObject = snob;
			this.contentElement = $(contentElement);
			this.setOptions(options);
		},
		
		/*
		 * build it
		 */
		build: function() {
			this.colcount = 0;
			
			this.table = new Element("table");
			this.table.cellpadding = this.cellpadding;
			this.table.cellspacing = this.cellspacing;
			this.table.width="100%";
			
			this.tr = this.table.insertRow(-1);
			
			for(d=0;d<this.SNObject.getArray().length;d++) {
				this.cdbo = this.SNObject.getArray()[d];
				if (this.colcount < this.columns) {
					this.td = this.tr.insertCell(-1);
					this.colcount++;
					// create box
					npt = new Element('input');
					npt.type = 'checkbox';
					npt.name = this.options.boxname ? this.options.boxname : 'noname';
					npt.id = this.options.idProperty 
						? this.cdbo.getProperty(this.options.idProperty) 
						: this.cdbo.getProperty(this.options.valueProperty);
					npt.value = this.options.valueProperty 
						? this.cdbo.getProperty(this.options.valueProperty) 
						: this.cdbo.getProperty(this.options.idProperty);
					if (this.options.boxClass) npt.className = this.options.boxClass;
					if (this.options.altProperty) {
						npt.alt = this.cdbo.getProperty(this.options.altProperty);
						npt.title = this.cdbo.getProperty(this.options.altProperty);
					}
					if (this.options.onchange) npt.onchange = function() {
						this.options.onchange(this.cdbo.getProperty(this.options.valueProperty));
					}
					
					this.td.appendChild(npt); 
				
					// display
					spn = new Element('span');
					if (this.options.displayClass) spn.className = this.options.displayClass;
					spn.innerHTML = this.cdbo.getProperty(this.options.displayProperty);
					
					this.td.appendChild(spn);
					
					// if we hit the max column count, insert row
					if (this.colcount == this.options.columns) {
						this.colcount = 0;
						this.tr = this.table.insertRow(-1); 
					}
				}
			}
			if (this.colcount < this.options.columns) {
				this.td.colspan = (this.options.columns-this.colcount+1);
			}
			
			this.contentElement.appendChild(this.table);
			return this.table;	
		}
		
	}), 
		
	
	/***************************************************************
	//	Originally adapted from O'Reilly's 'Javascript: The Definitive 
	//	Guide' and maintained from there.  Pass a form object to the 
	//	validate() function and it will iterate the elements list and 
	//	validate any text / hidden / password fields on the form.  
	//	Fields are assigned specific attributes beform the call to 
	//	perform specific edits.  Default attribute field definition
	//	is a required text field.  See list of supported attributes
	//	below for defaults and overrides.
	//
	//	David Connell   -  10/27/99
	//
	//***************************************************************
	//	Maintenance Logs
	//***************************************************************
	//  (dkc) 10/27/1999
	//			- Modified to report first error and return false.
	//
	//			- Added properties for invalid (not allowed) fields
	//			  and positive numeric fields.		
	//
	//  (dkc) 03/23/2000
	//			- Incorporated the date validation routines
	//			  (source adapted from DateValidation.js)
	//
	//			- Added optional range checking for date fields
	//
	//  (dkc) 11/03/2003
	//			- Added utility functions to retrieve select values
	//			- Added time format checking
	//
	//	(dkc) 07/02/2004
	//			- removed seconds from time format for SSI app...
	//
	//  (krn) 01/16/2010
	//			- Added to the DBO utility library.... THANKS!
	//
	//  (krn) 01/24/2010
	//			- Added parsing of "input" element attributes.
	//			- Validation attributes can be set to the html elements instead of javascript

	//  (krn) 01/30/2010
	//			- Changed "min" and "max" to "minval" and "maxval" due to google chrome globals
	 * 
	 *----------------------------------------------------------------
	//	Supported Field Attributes  
	//
	//		optional	Field may be empty ( boolean )
	//		invalid		Field must be empty	( boolean )
	//		numeric		Field must be a number ( boolean )	
	//		positive	Numeric field must be >= 0 ( boolean )
	//		minval		Numeric field lower range boundry ( numeric )
	//		maxval		Numeric field upper range boundry ( numeric )
	//		minDate		Date field lower range boundry ( MDYDate )
	//		maxDate		Date field upper range boundry ( MDYDate )
	
	//		-- Format Edits  --
	//		isMDYDate	Field must be a date in m/d/yyyy format	
	//    	isTime		Field must be valid time element in hh:mm
	//    	isMilTime	Field must be valid time element  in hh:mm[:ss]  (24 hour clock)
	//		isTimeAMPM	Field must be valid time element (including am/pm text) in hh:mm am
	//		isPhone		Requires 10 numerics and allows phone format chars
	//		isEMail		Proper format for internet email
	//		isZip		Checks for standard or Zip+4 formats
	//		isState 	Validated against static variable of 2 char 
	//						state abbreviations 
	//		isSSN		Checks for Social Sec # format
	//
	//		Note: using a range and a type is redundant, if a range is 
	//			  specified that range type is assumed.  I.e.  if you 
	//			  specify a minDate you don't have to specify isMDYDate
	//			  as true also.
	//				
	*/
	validation: { 
		//--------------------------------------------------------------
		// initialize variables
		init: function() {
			this.unitedStatesAbbreviations = "AL|AK|AS|AZ|AR|CA|CD|CN|CO|CT|DE|DC|FL|GA|GU|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|PR|RI|SC|SD|TN|TX|VI|UT|VT|VA|WA|WV|WI|WY";
			mailStates = this.unitedStatesAbbreviations.split("|"); 
			
		},
		//----------------------------------------------------------------
		//  getSelectValue()
		//  retrieve the value of the selected option from
		//  a select tag.
		getSelectValue: function(form,fldName) {
				// get the field from the form
				fld = form.elements[fldName];
				rv = -1;
				
				// if the field was valid and it was a selection,
				if (fld && fld.options)
				{
					// get the value of the selected option
					ndx = fld.selectedIndex;
					rv = fld.options[ndx].value;
				}
				
				// return results
				return rv;
		},
			
		//----------------------------------------------------------------
		// isBlank()
		//	 A utility function that returns true if a string contains only 
		//   whitespace characters.function isblank(s)
		isBlank: function(s){
		    for(i = 0; i < s.length; i++) {
		        c = s.charAt(i);
		        if ((c != ' ') && (c != '\n') && (c != '\t')) return false;
		    }
		    return true;
		},
		
		//----------------------------------------------------------------------
		// parseDate ( )
		//   extract the date string from the value member
		//   and parses it into it's 'm,d & y' components.  Can be 
		//	 used directly or as a helper function for validDate.
		//
		// Return:
		//		True if successfully parsed, false if not.
		//
		parseDate: function(dtObj){
			// date parsers
			slashpos1, slashpos2, lastpos;
		
			// the date string
			date = dtObj . value;		
			
			// parse month
			if (date.substring(1,2) != "/"){
				if (date.substring(0,1) < "0" || date.substring(0,1) > "9"){ return false; }
				if (date.substring(1,2) < "0" || date.substring(1,2) > "9"){ return false; }
				if (date.substring(0,2) <= "0" || date.substring(0,2) > 12){ return false; }
				if (date.substring(2,3) != "/"){ return false; }
				// set month member
				dtObj . month = date.substring(0,2);
				slashpos1 = 3;
			} else {	
				if (date.substring(0,1) < "1" || date.substring(0,1) > "9") { return false; }
				
				// set month member
				dtObj . month = date.substring(0,1);
				slashpos1 = 2;
			} 
			
			// parse day
			if (date.substring(slashpos1+1,slashpos1+2) != "/") { 
				if (date.substring(slashpos1,slashpos1+1) < "0" || date.substring(slashpos1,slashpos1+1) > "9"){ return false; }
				if (date.substring(slashpos1+1,slashpos1+2) < "0" || date.substring(slashpos1+1,slashpos1+2) > "9"){ return false; }
				if (date.substring(slashpos1+2,slashpos1+3) != "/"){ return false; }
				
				// set day member
				dtObj . day = date.substring(slashpos1, slashpos1+2);
				slashpos2 = slashpos1 + 3;
			} else {	
				if (date.substring(slashpos1,slashpos1+1) < "1" || date.substring(slashpos1,slashpos1+1) > "9"){ return false; }
		
				// set day member
				dtObj . day = date.substring(slashpos1, slashpos1+1);
				slashpos2 = slashpos1 + 2;
			} 
			
			// parse year ( 4 digits for year representation )
			lastpos = slashpos2+3;
			if ((date.substring(slashpos2,date.length)).length > 4)	{ return false; }
			for (i=(slashpos2); i<=(lastpos);i++) {	
				temp = date.substring(i,i+1);
				if (temp < "0" || temp > "9")
				{	return false;
				}
			}
		
			// set year member
			dtObj . year = date.substring(slashpos2,lastpos+1)	
			
			// test for valid day in month
			if (dtObj . month == 1 || dtObj . month == 3 || 
			    dtObj . month == 5 || dtObj . month == 7 || 
			    dtObj . month == 8 || dtObj . month == 10 || 
			    dtObj . month == 12)
			    {	if ( dtObj . day > 31 )
					{	return false;
					}
				}
				else
				{	if ( dtObj . month != 2 )
					{	if ( dtObj . day > 30 )
						{	return false;
						}
					}
					else //month = 2
					{	if ((dtObj . year % 4) != 0)	//NOT LEAP YEAR
						{	if (dtObj . day > 28)
							{	return false;
							}	
						}
						else //LEAP YEAR
						{	if (dtObj . day > 29)
							{	return false;
							}
						}
					}
				}
		
			// build hash value for comparisons
			dtObj . nVal = ( dtObj . year * 10000 ) + ( dtObj . month * 100 ) + dtObj . day
		
			// all done
			return true;
		},
		
		//----------------------------------------------------------------------
		// validTimeAMPM()
		//	evaluates a time (using validTime())
		//		plus ensures an AM/PM follows with a space.
		//
		validTimeAMPM: function ( e ) {
		 	vals = e.value.slice().split(" ");
		 	
		 	if (vals.length != 2)
		 		return "Invalid Time Format (HR:MI AM)";
		 	
		 	msg = validTime( vals[0] );
		 	
		 	if (msg.length < 1) {
		 		if (vals[1].toUpperCase() != "AM" && vals[1].toUpperCase() != "PM")
		 			msg = "Invalid Time Format (HR:MI AM)";
		 	}
		 	
		 	return msg;
		},
		
		//----------------------------------------------------------------------
		// validTime()
		//   parses value for 2-3 elements within proper ranges
		//	 
		validTime: function ( e ){
			hr = -1;
			min = -1;
			msg = "";
			err = 0;
			
			mxHour = (e.isMilTime ? 23 : 12);
			 mnHour = (e.isMilTime ? 0 : 1);
			vals = null;
			
			if (e.value)
				vals = e.value.split(":");
			else
				vals = e.split(":");
			
			// checks by length
			if (vals.length == 2)
			{
				if (vals[0].length < 1 || vals[1].length != 2)
					err = 1
				else {
					
					try {
						hr = vals[0];
						min = vals[1];
						
						if (isNaN(hr) || isNaN(min))
							return "Invalid Time Format (hh:mm)";
								
						// check agains constraints
						if ( (hr > mxHour ) ||
						     (hr < mnHour) ||
					    	 (min < 0) ||
					     	(min > 59) )
					     		
					     		err = 1;	
					} catch (e) {
						
					}
					
				}
			}
			else
				err = 1;
				
			if (err == 1)     
			     msg = "Invalid Time Format (hh:mm)";
			     
			return msg;	     
			
		},
		//----------------------------------------------------------------------
		// validDate ( )
		//   calls parseDate() to extract and parse the string dateand then 
		//	 does any specified range checks.
		//
		// Return:
		//	 Error message suitable for validform() or null if all OK.
		//
		validDate: function( dtObj ){
			// attempt to parse the date
			if ( !util.form.validation.parseDate( dtObj ) )
				return ( "Invalid date format for " + dtObj . name + " ( Use 'mm/dd/yyyy' ) ." );
			
			// range testing (min)	
			if ( dtObj . minDate )
			{	
				// we need another date obj
				dtMin = new Object();
				
				// extract it from original
				dtMin . value = dtObj . minDate
				
				// is it a good date
				if ( !util.form.validation.parseDate( dtMin ) )
					return ( "Invalid Minimum Date Format ( " + dtObj . name + " )." );
		
				// do the test
				if ( dtMin	. nVal > dtObj . nVal )
					return ( dtObj . name + " must be greater than " + dtObj . minDate + "." );
			}		
		
			// range testing ( max )	
			if ( dtObj . maxDate )
			{	
				// we need another date obj
				dtMax = new Object();
				
				// extract it from original
				dtMax . value = dtObj . maxDate
				
				// is it a good date
				if ( !util.form.validation.parseDate( dtMax ) )
					return ( "Invalid Minimum Date Format ( " + dtObj . name + " )." );
		
				// do the test
				if ( dtMax	. nVal < dtObj . nVal )
					return ( dtObj . name + " must be less than " + dtObj . maxDate + "." );
			}		
			
			// if we get here, return null as OK
			return;
		},	
		//--------------------------------------------------------------------------	
		// This is the function that performs form verification. It should be invoked
		// (directly or otherwise) from the onSubmit() event handler of the form.
		// The handler should return whatever value this function returns.
		validate: function(f){   
			/**
			 * parse input elements to add attribute tag validation..
			 * 
			 * krn - 01/24/2010
			 */
			tags = f.getElementsByTagName('input');
			for (t=0;t<tags.length;t++) {
				try {
					el = f.elements[tags[t].attributes['name'].value];
					atts = tags[t].attributes;
					for (a=0;a<atts.length;a++) {
						el[atts[a].name] = atts[a].value;
					}
				}catch(e) {}
			}
			
			var msg;    
			var errFlg = 0;
		
			// Loop through the elements of the form, looking for all 
			// text and textarea elements.  If found, begin validations.
		    	// If an error is found, a message will be displayed and the
		    	// function will exit with a false return code.    
			for(this.i = 0; this.i < f.elements.length; this.i++)     
			{
				var e = f.elements[this.i];        
				var mtFlg = 0;        
				var numFlg = 0;
				
		        	// first determine if it is a text based input         
				if ((e.type == "text") || (e.type == "textarea") || e.type == "password")
				{
					var name = e.id.replace(/_/g," ");			
					
					// does the field contain any data
					var mtFlg = ((e.value == null) || (e.value == "") || util.form.validation.isBlank(e.value)) ? true : false;
		
					// empty field			
					if (mtFlg)			
					{
						
						// required field??				
						if ((!e.optional && !e.optional == "1") && !e.invalid) {
							msg = "Please enter a value for " + name + ".";
						}			
					} else {
						// dissallowed field?? (conditioned before call)
						if ( e.invalid ) {
							msg = "Entry for " + name + " is not allowed with current options.";
						}
						
						// Now check numeric validations.
						if (e.numeric || e.minval || e.maxval || e.positive){
		
							// valid number edit
							if (isNaN(e.value)) 
							{
								msg = "The field " + name + " must be a number" ;
								// show error message
								util.form.validation.validationErrorMessage(msg);
		
								// set focus back to the field
								e.focus();
		
								// and return failure flag
								return false;
		
							}
							v = parseFloat(e.value);
						    
							// lower range check 						
							if ((e.minval != null) && (v < e.minval))
								msg = "The field " + name + " must be greater than " + e.minval;
							// upper range check
							if ((e.maxval != null) && (v > e.maxval))
								msg = "The field " + name + " must be less than " + e.maxval;
							// non-negative edit
							if ( e.positive  &&  ( v < 0.0 ) )
								msg = "The field " + name + " cannot be negative.";
						}
						 	
						// location oriented edits
						// test for zip / zip+4
						if (e.isZip){
							// start with length
							x = e.value.length;
							
							// check for zip+4 format
							if (x == 10){
								// split zip  / +4
								a = e.value.split('-');
								
								// check it out
								if ((a[0].len != 5) || isNaN(a[1]) || isNaN(a[2]) )
									msg = "Invalid Zip Code Format in " + name + ". [ xxxxx or xxxxx-xxxx ]";
							} else if ((x != 5) || isNaN(e.value)){
								msg = "Invalid Zip Code Format in " + name + ". [ xxxxx or xxxxx-xxxx ]";
							}
						}
						
						if (e.isState){
							// format to fit the variable pattern
							found = 0;
							for(ab=0;ab<util.form.validation.mailStates.length;ab++)
								if(e.value.toUpperCase() == util.form.validation.mailStates[ab])
									found ++;
									
							if (found == 0)
								msg = "Invalid State Abbreviation: " + name ;
						}				
						
						// test phone numbers for 10 numerics and 
						// allowed punctuation				
						if (e.isPhone){				
							// we're looking for 10
							cnt = 0;
							
							// get entry data
							txt = e.value;
							
							// test each character
							for(x=0;x<txt.length;x++){
								// extract char
								tst = txt.substr(x,1);
								
								// and test it form numerics
								if ( (tst >= '0') && (tst <= '9') )
									// bump counter
									cnt++;							
								else
									// test from allowed punctuation
									if ( '()-. '.indexOf(tst) == -1){
										// set flags and exit condition
										cnt = -1;
										x = txt.length;
									}
															
							}		
							
							// check for any right numerics count
							if (cnt != 10)
								// oops...
								msg = "Invalid Phone Number Format in " + name + ". [ (xxx) xxx-xxxx ]";
								
						}
						
						// test email formats
						if (e.isEmail){
							// assume good result
							rv = 1;
							
							// split domain from user name
							p1 = e.value.split("@");
							
							// and check it
							if (p1.length == 2){
								// split the domain from top domain
								p2 = p1[1].split('.');
							
								// and check it
								if (p2.length < 2)
									// invalid domain
									rv = 0;
							}		
							else
								// invalid format
							  	rv = 0;
							
							if (rv == 0)
								msg = "Invalid Email Format. [ xxxx@xxxxx.xxx ]";
						}
						
						if (e.isSSN){
							pts = e.value.split('-');
							if ((pts.length != 3) || (pts[0].length != 3) || 
							    (pts[1].length != 2) || (pts[2].length != 4))
								msg = "Invalid SSN Format. [ xxx-xx-xxxx ]";
					
						}			
						
						// Date checks (All dates expected in mm/dd/yyyy format)
						if ( e.isMDYDate || e.minDate || e.maxDate )
							msg = util.form.validation.validDate ( e );
						
						// Time checks 					
						if ( e.isTime || e.isMilTime ) 
							msg = util.form.validation.validTime( e );
						if ( e.isTimeAMPM)
							msg = util.form.validation.validTimeAMPM( e );
					}
					
					//---------------------------
					// evaluate the situation
					if ((msg != null) && (msg.length > 0)){
						// show error message
						util.form.validation.validationErrorMessage(msg);
		
						// set focus back to the field
						e.focus();
		
						// and return failure flag
						return false;
					}	
				}
			}
		    alert('form validated successfully');
		    // all well that ends well    
			return true;
			
		},
		
		validationErrorMessage: function( msg){
			err_msg  = "______________________________________________________\n\n";
			err_msg += "The form was not submitted for the following reason.\n";
			err_msg += "Please correct the error and re-submit the form.\n";
			err_msg += "______________________________________________________\n\n";
			err_msg += msg;	
		
			alert(err_msg);
		
			return false;
		}
		    
	},
	validate: function(frm) {
		return this.validation.validate(frm);
	}		
	
}
